home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC Media 22
/
PC MEDIA CD22.iso
/
share
/
prog
/
datalib2
/
index2.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1995-08-14
|
9KB
|
353 lines
#include "datapriv.hpp"
/*
This file contains the routines concerned with the generation and use
of index files, and contains the routines for writing a new or changed
record, and finding a record in the index file
*/
/****************************************************************************
* *
* The following routines are all coupled to records *
* *
****************************************************************************/
/********************************************************************
Index Creation Stuff
*********************************************************************/
// Check an index expression and return its length
int record::indchk(char *ex,int &rtype)
{
char *wsp;
op *rv;
if (!db->ex)
{
db->ex=new expval(db);
db->exwork=new char[512];
}
if (!db->ex || !db->exwork || db->ex->erflag==2) return 0;
db->ex->erflag=0;
wsp=db->exwork;
db->ex->exetoken(ex,&wsp);
if (db->ex->erflag==1) {db->ex->erflag=3; return 0;} // show tokeniser error
wsp=db->exwork;
indflg=1;
rv=db->ex->exeval(&wsp,this);
if (db->ex->erflag) return 0;
rtype=rv->optype;
if (rtype==OPINT) return 8;
if (rtype==OPSTR)
{
if (rv->oplen<1) strcpy(db->ers,"Index length must be greater than 0");
if (rv->oplen>100) strcpy(db->ers,"Index length must be 100 or less");
return (rv->oplen>0 && rv->oplen<101) ? rv->oplen : 0;
}
if (rtype==OPDATE) return 8;
strcpy(db->ers,"No Logical expr. in index"); return 0; // Must be logical
}
/********************* RECORD::GETIND ************************/
// Get the specified index record, we know already that there is one
// 'cos the calling function has checked the bounds.
long record::getind(long n)
{
long ln=n; // Local copy of n
struct indpt *npt; // Useful pointer
int dirn=1; // + or - 1 depending on next/previous
if (n==FIRST || n==LAST) // Return to top level indclus
{
while(curind->icp->parent)
{
npt=curind;
curind=curind->parent;
delete npt;
}
if (n==FIRST)
{ln=NEXT; curind->currec=-1;} // FIRST
else
{curind->currec=curind->icp->nclusrec; ln=PREVIOUS;} // LAST
}
if (ln==PREVIOUS) dirn=-1;
while (1) // Select next record
{
if (!curind->icp->clustyp) // Is this a record cluster ?
{
if ((ln==NEXT && curind->currec<curind->icp->nclusrec-1) ||
(ln==PREVIOUS && curind->currec>0)) // Get next record
{
curind->currec+=dirn;
long rv=*(long *)(curind->icp->clusdat+8+curind->currec*curind->icp->reclen);
return(rv);
}
else // Run out of records in this cluster
{
if (!curind->parent) return 0;
npt=curind->parent; delete curind; // Get parent record
curind=npt;
}
}
else // We have a pointer cluster
{
long nclus;
if ((ln==NEXT && curind->currec<curind->icp->nclusrec-1) ||
(ln==PREVIOUS && curind->currec>0)) // Get next record
{
curind->currec+=dirn;
nclus=*(long *)(curind->icp->clusdat+4+curind->currec*curind->icp->reclen);
curind=new indpt(nclus,curind,curind->icp->oi);
if (ln==PREVIOUS) curind->currec=curind->icp->nclusrec;
}
else // Go back to parent
{
if (!curind->parent)
{
if (ln==NEXT) getind(LAST); else getind(FIRST);
return 0;
}
npt=curind->parent; delete curind;
curind=npt;
}
}
}
}
/********************** SELKEY ********************************/
// Select record by key
int record::selkey(double value,int type)
{
char ws[10];
*(double *)ws=value;
return selkey(ws,type,OPINT);
}
// Character or Double, flag shows which
// Note in this function if type is READIND then the record is not read
// from disk,l this is used purely to update the index pointers for a
// newly written record
int record::selkey(char *value,int type,int num)
{
int i;
int ret=-1;
if (!curind) return CANTSEL;
memcpy(lkey,value,curind->icp->explen+1);
ltype=type;
lseltype=num;
indpt *npt; // Now go back up to top level index
while(curind->parent) {npt=curind; curind=curind->parent; delete npt;}
delete curind;
curind=new indpt(oi->topind);
curind=curind->selkey(value,num,ret,i);
rn=*(long *)(curind->icp->clusdat+8+i*curind->icp->reclen);
if (type!=READIND) select(rn,ALL,1);
while((type==DEL && *recbuf==' ') || (type==NOTDEL && *recbuf=='*'))
{
if (ret=selkey()) return ret;
}
return ret;
}
// Get the next record which matches the current record key
int record::selkey(void)
{
char *keyp;
if (ltype==-1) return CANTSEL;
do
{
long tn;
if (!(tn=getind(NEXT))) return CANTSEL;
keyp=curind->icp->clusdat+(curind->currec)*curind->icp->reclen+12;
if ((lseltype!=OPINT && strcmpdb(keyp,lkey,curind->icp->explen)) ||
(lseltype==OPINT && *(double *)lkey-*(double *)keyp)) return NOKEY;
rn=tn;
select(rn,ALL,1);
}
while((ltype==DEL && *recbuf==' ') || (ltype==NOTDEL && *recbuf=='*'));
return 0;
}
// Delete a key from the record
// Use a new record to find this records key in the index file, when
// we find it delete the key
void record::delkey(char *oldkey,index *ip)
{
record rec(*db,ip->name); // Create record used to search index file
rec.selkey(oldkey,ALL,ip->indtype); // Now find this record in the index file
while(rec.rn!=rn)
{
if (rec.selkey())
{
dbfer(NODELKEY);
return;
}
}
rec.curind->icp->subtract(rec.curind->currec); // Delete key from tree
}
// Insert a new key from the current record
void record::inskey(char *newkey,index *ip)
{
int rsk; // Return value from selkey
int irn; // Record value found by selkey
indpt *clin=new indpt(ip->tblk,0,ip); // Cluster to insert key
clin=clin->selkey(newkey,ip->indtype,rsk,irn); // Find nearest key in index
clin->icp->add(newkey,rn,irn,rsk); // Add the new key
indpt *nin;
while(clin->parent) {nin=clin->parent; delete clin; clin=nin;} delete clin;
}
/**************************************************************************
Routines concerned with the index structure
**************************************************************************/
index::~index()
{
if (chng)
{
*(long *)fclus=tblk;
*(long *)(fclus+4)=lblk;
Fseek(fp,0,SEEK_SET);
Fwrite(fclus,512,1,fp);
}
indclus *ip,*nip;
ip=topind; while(ip) {nip=ip->next; delete ip; ip=nip;}
fclose(fp);
}
/***************** Add an index to the database file ********************/
int database::addindex(char *iname)
{
index *ip,*nip,*lip;
char ws[128],*fcp,*wsp;
FILE *fp;
strcpy(ws,iname);
if (wsp=strchr(ws,'.')) *wsp=0; strcat(ws,".NDX");
if (!(fp=fopen(ws,"r+b"))) return NOINDEX;
*strchr(ws,'.')=0;
if (!(wsp=strrchr(ws,'\\'))) wsp=ws; else wsp++;
strupr(wsp);
if (getindex(wsp)) {delete ip; return INVIND;}
ip=new index;
ip->next=0;
ip->fp=fp;
strcpy(ip->fname,iname); // Open files & enter names
strcpy(ip->name,wsp);
fcp=ip->fclus;
Fread(fcp,512,1,fp); /// Read the header cluster
ip->nclus=0;
ip->topind=0;
ip->topind=new indclus(*(long *)fcp,0,ip);
ip->topind->reclen=*(int *)(fcp+INDLEN); // Length of index
ip->topind->explen=*(int *)(fcp+INDELEN); // Expression length
ip->tblk=*(long *)(fcp); // Top Block
ip->lblk=*(long *)(fcp+4); // Last Block
ip->maxclusrec=*(long *)(fcp+14); // Max records/cluster
ip->firstrec=0; // First record using cluster
ip->chng=0; // Change flag
strcpy(ip->indexp,fcp+INDEXP); // Index expression
if (!ex)
{
ex=new expval(this);
exwork=new char[512];
}
if (!ex || !exwork || ex->erflag==2) return NOINDEX;
wsp=ip->tindexp;
ex->erflag=0;
ex->exetoken(fcp+INDEXP,&wsp); // Tokenise it
if (ex->erflag)
{
if (ex->erflag==1) ex->erflag=3; // Invalid index expression
delete ip;
return INVIND;
}
ip->indtype=(*(int *)(fcp+INDTYPE)) ? OPINT : OPSTR; // Index type
switch(*(fcp+9))
{
case 'N' : ip->restype=OPINT; break;
case 'D' : ip->restype=OPDATE; break;
case 'C' : ip->restype=OPSTR; break;
default : dbfer(INVIND); break;
}
// Finally we got here so link the index into the tree
if (!findex) findex=ip; // First index
else
{
lip=findex; nip=findex->next;
while(nip) {lip=nip; nip=lip->next;} // Add to end of tree
lip->next=ip;
}
return 0;
}
// Remove an index from this database
int database::subindex(char *name)
{
index *nip,*lip,*ip;
if (!(ip=getindex(name))) return NOINDEX;
if (ip==findex) findex=ip->next;
else
{
lip=findex;
nip=findex->next;
while(nip!=ip) {lip=nip; nip=lip->next;}
lip->next=nip->next;
}
delete ip;
return 0;
}